package cn.nkpro.elcube.components.pipeline.services;

import cn.nkpro.elcube.annotation.NkNote;
import cn.nkpro.elcube.co.NkAsyncInvoker;
import cn.nkpro.elcube.co.spel.NkAbstractHttpSpELInjection;
import cn.nkpro.elcube.co.http.NkHttpResponse;
import cn.nkpro.elcube.data.redis.RedisSupport;
import cn.nkpro.elcube.exception.NkHttpException;
import cn.nkpro.elcube.exception.NkInputFailedCaution;
import cn.nkpro.elcube.security.SecurityUtilz;
import cn.nkpro.elcube.utils.DateTimeUtilz;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 企查查
 */
@ConditionalOnProperty({"nk.third.qcc.key","nk.third.qcc.secretKey"})
@Component("SpELqcc")
public class NkSpELQcc extends NkAbstractHttpSpELInjection {


    @Value("${nk.third.qcc.key}")
    private String key;

    @Value("${nk.third.qcc.secretKey}")
    private String secretKey;

    @Value("${nk.third.qcc.expire:86400}")
    private Long expire = 60*60*24L;

    /**
     * 单位秒
     */
    @Value("${nk.third.qcc.timeout:30}")
    private Integer timeout = 30;

    private String apiUrl = "http://api.qichacha.com";

    @Autowired
    private RedisSupport<JSONObject> redis;
    @Autowired
    private NkAsyncInvoker asyncInvoker;

    @NkNote(value="企查查-企业基本信息",desc="@qcc.ECIComplement$GetInfo([searchKey])")
    public Object ECIComplement$GetInfo(String searchKey){
        return get(String.format("/ECIComplement/GetInfo?searchKey=%s",searchKey));
    }

    @NkNote(value="企查查-949开户尽职调查列表",desc="@qcc.AcctScan$GetInfo([searchKey])")
    public Object AcctScan$GetInfo(String searchKey){
        return get(String.format("/AcctScan/GetInfo?searchKey=%s",searchKey));
    }

    @NkNote(value="企查查-949严重违法失信详情",desc="@qcc.AcctScan$GetAcctSerVioDetail([detailId])")
    public Object AcctScan$GetAcctSerVioDetail(String detailId){
        return get(String.format("/AcctScan/GetAcctSerVioDetail?id=%s",detailId));
    }

    public boolean isNotBlank(String str){
        return StringUtils.isNotBlank(str)&&!StringUtils.equalsAnyIgnoreCase(str,"null","undefined");
    }

    // 在此之前增加接口方法

    @SneakyThrows
    public JSONObject get(String url){
        return redis.getIfAbsent(hashCode(url,null,null),expire,true,()->{
            String time = String.valueOf(DateTimeUtilz.nowSeconds());
            String token = DigestUtils.md5Hex(key.concat(time).concat(secretKey)).toUpperCase();

            Map<String,String> headers = new HashMap<>();
            headers.put("Token",token);
            headers.put("Timespan",time);

            String getUrl = apiUrl +url + (url.contains("?")?'&':'?') +"key=" + key;

            NkHttpResponse response = doGet(getUrl, headers);

            // todo 这里记录调用日志到数据库

            if(response.getStatusCode()==200){
                JSONObject responseJson = JSON.parseObject(response.getBody());
                switch (responseJson.getInteger("Status")){
                    case 200://【有效请求】查询成功

                        if(responseJson.containsKey("Paging")){

                            // 获取返回结果列表
                            JSONArray responseList = null;
                            Object result = responseJson.get("Result");
                            if(result instanceof JSONObject){
                                result = ((JSONObject) result).getJSONArray("Data");
                            }
                            if(result instanceof JSONArray){
                                responseList = (JSONArray) result;
                            }
                            if(responseList!=null){

                                // 计算总页数
                                JSONObject paging = responseJson.getJSONObject("Paging");
                                long totalRecords = paging.getLong("TotalRecords");
                                long pageSize = paging.getLong("PageSize");
                                // 最多取10页
                                long pageTotal = Math.min(totalRecords/pageSize + (totalRecords%pageSize > 0 ? 1 : 0),10);

                                // 同步调用方式开始
                                List<Future<JSONObject>> futures = new ArrayList<>();
                                for(int i=2;i<=pageTotal;i++){
                                    final int finalPage = i;
                                    futures.add(
                                            asyncInvoker.async(
                                                    SecurityUtilz.getUser(),
                                                    ()->
                                                    JSON.parseObject(
                                                            doGet(String.format("%s&pageIndex=%d",getUrl,finalPage),headers)
                                                                    .getBody()
                                                    )
                                            )
                                    );
                                }
                                JSONArray finalResponseList = responseList;
                                futures.forEach(future -> {
                                    try {
                                        JSONObject responsePaging = future.get(timeout, TimeUnit.SECONDS);
                                        if(responsePaging!=null){
                                            // 判断企查查返回的数据结构是Result 还是Result-Data
                                            Object resultPaging = responsePaging.get("Result");
                                            if(resultPaging instanceof JSONObject){
                                                resultPaging = ((JSONObject) resultPaging).getJSONArray("Data");
                                            }
                                            if(resultPaging instanceof JSONArray){
                                                finalResponseList.addAll((JSONArray) resultPaging);
                                            }
                                        }
                                    } catch (InterruptedException | TimeoutException | ExecutionException e) {
                                        throw new NkHttpException(e.getMessage(),e);
                                    }
                                });
                            }

                            // 同步调用方式结束
                        }

                        return responseJson;
                    case 201://【有效请求】查询无结果
                        return null;
                    case 202:throw new NkInputFailedCaution("企查查 -【有效请求】查询参数错误，请检查");
                    case 205:throw new NkInputFailedCaution("企查查 -【有效请求】等待处理中");
                    case 207:throw new NkInputFailedCaution("企查查 -【有效请求】请求数据的条目数超过上限（5000）");
                    case 208:throw new NkInputFailedCaution("企查查 -【有效请求】此接口不支持此公司类型查询");
                    case 209:throw new NkInputFailedCaution("企查查 -【有效请求】企业数量超过上限");
                    case 213:throw new NkInputFailedCaution("企查查 -【有效请求】参数长度不能小于2");
                    case 215:throw new NkInputFailedCaution("企查查 -【有效请求】不支持的查询关键字");
                    case 218:throw new NkInputFailedCaution("企查查 -【有效请求】该企业暂不支持空壳扫描");
                    case 219:throw new NkInputFailedCaution("企查查 -【有效请求】该企业暂不支持开户尽调");
                    case 105:throw new NkInputFailedCaution("企查查 -【有效请求】接口已下线停用");
                    case 110:throw new NkInputFailedCaution("企查查 -【有效请求】当前相同查询连续出错，请等2小时后重试");
                    case 101:throw new NkInputFailedCaution("企查查 -【无效请求】当前的KEY无效或者还未生效中");
                    case 102:throw new NkInputFailedCaution("企查查 -【无效请求】当前KEY已欠费");
                    case 103:throw new NkInputFailedCaution("企查查 -【无效请求】当前KEY被暂停使用");
                    case 104:throw new NkInputFailedCaution("企查查 -【无效请求】请求KEY异常，请联系管理员");
                    case 106:throw new NkInputFailedCaution("企查查 -【无效请求】非法请求过多，请联系管理员");
                    case 107:throw new NkInputFailedCaution("企查查 -【无效请求】被禁止的IP或者签名错误");
                    case 108:throw new NkInputFailedCaution("企查查 -【无效请求】异常请求过多，请联系管理员");
                    case 109:throw new NkInputFailedCaution("企查查 -【无效请求】请求超过每日系统限制");
                    case 111:throw new NkInputFailedCaution("企查查 -【无效请求】接口权限未开通，请联系管理员");
                    case 112:throw new NkInputFailedCaution("企查查 -【无效请求】您的账号剩余使用量已不足或已过期");
                    case 113:throw new NkInputFailedCaution("企查查 -【无效请求】当前接口已被删除，请重新申请");
                    case 114:throw new NkInputFailedCaution("企查查 -【无效请求】当前接口已被禁用，请联系管理员");
                    case 115:throw new NkInputFailedCaution("企查查 -【无效请求】身份验证错误或者已过期");
                    case 116:throw new NkInputFailedCaution("企查查 -【无效请求】请求超过每日调用总量限制");
                    case 117:throw new NkInputFailedCaution("企查查 -【无效请求】当前不支持的请求参数调用量过多");
                    case 118:throw new NkInputFailedCaution("企查查 -【无效请求】当前接口不支持此方式的调用");
                    case 119:throw new NkInputFailedCaution("企查查 -【无效请求】您的帐号出现异常，请联系管理员");
                    case 120:throw new NkInputFailedCaution("企查查 -【无效请求】系统流量异常，请稍后再试");
                    case 199:throw new NkInputFailedCaution("企查查 -【无效请求】系统未知错误，请联系技术客服");
                    case 203:throw new NkInputFailedCaution("企查查 -【无效请求】系统查询有异常，请联系技术人员");
                    case 214:throw new NkInputFailedCaution("企查查 -【无效请求】您还未购买过该接口，请先购买");
                    case 223:throw new NkInputFailedCaution("企查查 -【无效请求】当前接口的计费方式不支持此查询参数");
                    default: throw new NkInputFailedCaution("企查查-未知错误请求");
                }
            }
            throw new NkInputFailedCaution("企查查-未知错误请求");
        });
    }


    /**
     * 董监高遍历查询,查出所有页数的数据
     * @return
     */
    @SneakyThrows
    public JSONObject loopGet(String url, Object arrayParam) {
        Stream<String> stream = null;
        if (arrayParam == null) {
            return null;
        } else if (arrayParam instanceof Collection) {
            stream = ((Collection) arrayParam).stream()
                    .map(Object::toString);
        } else if (arrayParam.getClass().isArray()) {
            stream = Arrays.stream((String[]) arrayParam);
        }
        assert stream == null : "请求参数不能为空！";

        String perName = stream.sorted().collect(Collectors.joining(","));
        Map<String, String> param = new HashMap<>();
        param.put("perName",perName);

        return redis.getIfAbsent(hashCode(url, null, param), expire, true, () -> {
            //这是方法返回的参数
            JSONObject res = new JSONObject();
            //这是每个人返回的所有数据的集合
            JSONArray allInfo = new JSONArray();
            String[] perNames = perName.split(",");
            Arrays.stream(perNames).forEach(name -> {
                String time = String.valueOf(DateTimeUtilz.nowSeconds());
                String token = DigestUtils.md5Hex(key.concat(time).concat(secretKey)).toUpperCase();

                Map<String, String> headers = new HashMap<>();
                headers.put("Token", token);
                headers.put("Timespan", time);

                String getUrl = apiUrl + url + name + (url.contains("?") ? '&' : '?') + "key=" + key;

                NkHttpResponse response = doGet(getUrl, headers);
                //这个是单人返回的数据，自定义的参数有name:人的名称；statusCode：返回的code;list:返回的数据
                JSONObject singleResponseJson = new JSONObject();
                singleResponseJson.put("name", name);
                if (response.getStatusCode() == 200) {
                    singleResponseJson = JSON.parseObject(response.getBody());
                    if(singleResponseJson.containsKey("Paging")){
                        // 获取返回结果列表
                        JSONArray responseList = null;
                        Object result = singleResponseJson.get("Result");
                        if(result instanceof JSONObject){
                            result = ((JSONObject) result).getJSONArray("Data");
                        }
                        if(result instanceof JSONArray){
                            responseList = (JSONArray) result;
                        }
                        if(responseList!=null){

                            // 计算总页数
                            JSONObject paging = singleResponseJson.getJSONObject("Paging");
                            long totalRecords = paging.getLong("TotalRecords");
                            long pageSize = paging.getLong("PageSize");
                            // 最多取10页
                            long pageTotal = Math.min(totalRecords/pageSize + (totalRecords%pageSize > 0 ? 1 : 0),10);

                            // 同步调用方式开始
                            List<Future<JSONObject>> futures = new ArrayList<>();
                            for(int i=2;i<=pageTotal;i++){
                                final int finalPage = i;
                                futures.add(
                                        asyncInvoker.async(
                                                SecurityUtilz.getUser(),
                                                ()->
                                                        JSON.parseObject(
                                                                doGet(String.format("%s&pageIndex=%d",getUrl,finalPage),headers)
                                                                        .getBody()
                                                        )
                                        )
                                );
                            }
                            JSONArray finalResponseList = responseList;
                            futures.forEach(future -> {
                                try {
                                    JSONObject responsePaging = future.get(timeout, TimeUnit.SECONDS);
                                    if(responsePaging!=null){
                                        // 判断企查查返回的数据结构是Result 还是Result-Data
                                        Object resultPaging = responsePaging.get("Result");
                                        if(resultPaging instanceof JSONObject){
                                            resultPaging = ((JSONObject) resultPaging).getJSONArray("Data");
                                        }
                                        if(resultPaging instanceof JSONArray){
                                            finalResponseList.addAll((JSONArray) resultPaging);
                                        }
                                    }
                                } catch (InterruptedException | TimeoutException | ExecutionException e) {
                                    throw new NkHttpException(e.getMessage(),e);
                                }
                            });
                            singleResponseJson.put("list",finalResponseList);
                            allInfo.add(singleResponseJson);
                        }
                        // 同步调用方式结束
                    }
                }else{
                    singleResponseJson.put("statusCode",response.getStatusCode());
                    singleResponseJson.put("list",null);
                    allInfo.add(singleResponseJson);
                }
                res.put("results", allInfo);
            });
            return res;
        });
    }


}
